/*
 *         (C) COPYRIGHT NBI Limited
 *              ALL RIGHT RESERVED
 *
 * File        : fastflow/core/logging.h
 * Authors     : dzhang
 * Create Time : 2021/09/03 09:43:07
 * Description :
 *
 */

#ifndef __FASTFLOW_CORE_LOGGING_H___
#define __FASTFLOW_CORE_LOGGING_H___

#include <stdint.h>

#include <iostream>
#include <limits>
#include <sstream>
#include <atomic>
#include <chrono>
#include <climits>
#include <map>
#include <memory>
#include <mutex>
#include <string>

#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#if defined(__MACH__)
#include <pthread.h>
#else
#include <sys/syscall.h>
#endif
#endif

#include "fastflow/core/macros.h"

#define MIN_LOG_LEVEL_DEFAULT 0

// TODO(mrry): Prevent this Windows.h #define from leaking out of our headers.
#undef ERROR

namespace fastflow {

namespace profiling {

enum EventCategory {
  SESSION_EVENT = 0,
  NODE_EVENT,
  EVENT_CATEGORY_MAX
};

/*
Event descriptions for the above session events.
*/
static constexpr const char* event_categor_names_[EVENT_CATEGORY_MAX] = {
    "Session",
    "Node"};

/*
Timing record for all events.
*/
struct EventRecord {
  EventRecord(EventCategory category,
              int process_id,
              int thread_id,
              std::string event_name,
              long long time_stamp,
              long long duration,
              std::map<std::string, std::string>&& event_args) : cat(category),
                                                                           pid(process_id),
                                                                           tid(thread_id),
                                                                           name(std::move(event_name)),
                                                                           ts(time_stamp),
                                                                           dur(duration),
                                                                           args(event_args) {}
  EventCategory cat;
  int pid;
  int tid;
  std::string name;
  long long ts;
  long long dur;
  std::map<std::string, std::string> args;
};
}  // namespace profiling

const int DEBUG = 0;           // for debug
const int INFO = 1;            // base_logging::INFO;
const int WARNING = 2;         // base_logging::WARNING;
const int ERROR = 3;           // base_logging::ERROR;
const int FATAL = 4;           // base_logging::FATAL;
const int NUM_SEVERITIES = 5;  // base_logging::NUM_SEVERITIES;

namespace logging {
class Logger {
private:
    /* data */
public:
    Logger(/* args */);
    ~Logger();
};
}  // namespace logging

namespace Internal {

class LogMessage : public std::basic_ostringstream<char> {
public:
    LogMessage(const char *fname, int line, const char *func, int severity);
    ~LogMessage();

    // Returns the minimum log level for VLOG statements.
    // E.g., if min_vlog_level() is 2, then VLOG(2) statements will produce
    // output, but VLOG(3) will not. Defaults to 0.
    static int64 min_vlog_level();

    // Returns whether VLOG level lvl is activated for the file fname.
    //
    // E.g. if the environment variable TF_CPP_VMODULE contains foo=3 and fname is
    // foo.cc and lvl is <= 3, this will return true. It will also return true if
    // the level is lower or equal to TF_CPP_MIN_VLOG_LEVEL (default zero).
    //
    // It is expected that the result of this query will be cached in the VLOG-ing
    // call site to avoid repeated lookups. This routine performs a hash-map
    // access against the VLOG-ing specification provided by the env var.
    static bool vmodule_activated(const char *fname, int level);

protected:
    void generate_log_message();

private:
    const char *fname_;
    const char *funcname_;
    int line_;
    int severity_;
};

// Uses the lower operator & precedence to voidify a LogMessage reference, so
// that the ternary VLOG() implementation is balanced, type wise.
struct Voidifier {
    template <typename T>
    void operator&(const T &) const {}
};

// LogMessageFatal ensures the process will exit in failure after
// logging this message.
class LogMessageFatal : public LogMessage {
public:
    LogMessageFatal(const char *file, int line) FASTFLOW_ATTRIBUTE_COLD;
    FASTFLOW_ATTRIBUTE_NORETURN ~LogMessageFatal();
};

#ifdef DEBUGFATFLOW

#define VLOGD ::CC_NAME_SPACE_NAME::Internal::LogMessage(__FILE__, __LINE__, __func__, ::CC_NAME_SPACE_NAME::DEBUG)
#define VLOGI ::CC_NAME_SPACE_NAME::Internal::LogMessage(__FILE__, __LINE__, __func__, ::CC_NAME_SPACE_NAME::INFO)
#define VLOGW ::CC_NAME_SPACE_NAME::Internal::LogMessage(__FILE__, __LINE__, __func__, ::CC_NAME_SPACE_NAME::WARNING)
#define VLOGE ::CC_NAME_SPACE_NAME::Internal::LogMessage(__FILE__, __LINE__, __func__, ::CC_NAME_SPACE_NAME::ERROR)
#define VLOGF ::CC_NAME_SPACE_NAME::Internal::LogMessageFatal(__FILE__, __LINE__)

#else

#define VLOGD ::CC_NAME_SPACE_NAME::Internal::LogMessage("", 0, "", ::CC_NAME_SPACE_NAME::DEBUG)
#define VLOGI ::CC_NAME_SPACE_NAME::Internal::LogMessage("", 0, "", ::CC_NAME_SPACE_NAME::INFO)
#define VLOGW ::CC_NAME_SPACE_NAME::Internal::LogMessage("", 0, "", ::CC_NAME_SPACE_NAME::WARNING)
#define VLOGE ::CC_NAME_SPACE_NAME::Internal::LogMessage("", 0, "", ::CC_NAME_SPACE_NAME::ERROR)
#define VLOGF ::CC_NAME_SPACE_NAME::Internal::LogMessageFatal("", 0)

#endif

#define LOG(severity) _FASTFLOW_LOG_##severity

#ifdef IS_MOBILE_PLATFORM

// Turn VLOG off when under mobile devices for considerations of binary size.
#define VLOG_IS_ON(lvl) ((lvl) <= 0)

#else
// Otherwise, set TF_CPP_MIN_VLOG_LEVEL environment to update minimum log level
// of VLOG, or TF_CPP_VMODULE to set the minimum log level for individual
// translation units.
#define VLOG_IS_ON(lvl)                                                                                                \
    (([](int level, const char *fname) {                                                                               \
        static const bool vmodule_activated =                                                                          \
            ::CC_NAME_SPACE_NAME::Internal::LogMessage::vmodule_activated(fname, level);                               \
        return vmodule_activated;                                                                                      \
    })(lvl, __FILE__))

#endif

#ifdef DEBUGMAGIK
#define VLOG(level)                                                                                                    \
    FASTFLOW_PREDICT_TRUE(!VLOG_IS_ON(level))                                                                          \
    ? (void)0                                                                                                          \
    : ::CC_NAME_SPACE_NAME::Internal::Voidifier() &                                                                    \
            ::CC_NAME_SPACE_NAME::Internal::LogMessage(__FILE__, __LINE__, __func__, level)
#else
#define VLOG(level)                                                                                                    \
    FASTFLOW_PREDICT_TRUE(!VLOG_IS_ON(level))                                                                          \
    ? (void)0                                                                                                          \
    : ::CC_NAME_SPACE_NAME::Internal::Voidifier() & ::CC_NAME_SPACE_NAME::Internal::LogMessage("", 0, "", level)
#endif

#ifdef DEBUGMAGIK
#define VLOG(level)                                                                                                    \
    FASTFLOW_PREDICT_TRUE(!VLOG_IS_ON(level))                                                                          \
    ? (void)0                                                                                                          \
    : ::CC_NAME_SPACE_NAME::Internal::Voidifier() &                                                                    \
            ::CC_NAME_SPACE_NAME::Internal::LogMessage(__FILE__, __LINE__, __func__, level)
#else
#define VLOG(level)                                                                                                    \
    FASTFLOW_PREDICT_TRUE(!VLOG_IS_ON(level))                                                                          \
    ? (void)0                                                                                                          \
    : ::CC_NAME_SPACE_NAME::Internal::Voidifier() & ::CC_NAME_SPACE_NAME::Internal::LogMessage("", 0, "", level)

#endif

}  // namespace Internal

#define CHECK(condition)                                                                                               \
    if (FASTFLOW_PREDICT_FALSE(!(condition)))                                                                          \
    VLOGF << "Check failed: " #condition << " ==> "

#define CHECK_NE(val1, val2) CHECK(val1 != val2)
#define CHECK_EQ(val1, val2) CHECK(val1 == val2)

#define DCHECK(condition) CHECK(condition)

}  // namespace fastflow

#endif  // __FASTFLOW_CORE_LOGGING_H___